# imglib, a small library for working with RGB raster images
# by drummyfish, realeased under CC0 1.0, public domain
#
# Before including this library define the following:
#
# - IMG_mem: image memory, pointer to a block of memory in type env. 8
# - IMG_out: library output function, pops one value and outputs it
#
# You are responsible for not creating images that wouldn't fit in the reserved
# memory (image size is: width * height * 3). This library is simple! Drawing is
# primitive and done with naive algorithms.
#
# TODO:
#   - functions: IMG_fillRect, IMG_drawRect, IMG_flip, IMG_rotate

~_IMG_size:2
~8
  ~_IMG_ptr:0
~0

IMG_BLACK: 0 0 0 .
IMG_WHITE: 255 255 255 .
IMG_GRAY: 128 128 128 .
IMG_RED: 255 0 0 .
IMG_GREEN: 0 255 0 .
IMG_BLUE: 0 0 255 .
IMG_YELLOW: 255 255 0 .
IMG_PINK: 255 0 255 .

# Pops 2, creates new image with size $1 x $0.
IMG_new:
  $>_IMG_size $:_IMG_size $<_IMG_size $:_IMG_size
.

# Pushes current image width.
IMG_width:
  $_IMG_size
.

# Pushes current image height.
IMG_height:
  $>_IMG_size $_IMG_size $<_IMG_size
.

# Outputs image as ASCII art.
IMG_toASCII:
  ~8
    $IMG_mem>_IMG_ptr
  ~0

  IMG_height
  @'
    IMG_width

    @'
      "#OVaxsflc/!;,.- " 
      0 ~8 $_IMG_ptr >0 $>_IMG_ptr ~0
      0 ~8 $_IMG_ptr >0 $>_IMG_ptr ~0
      0 ~8 $_IMG_ptr >0 $>_IMG_ptr ~0
      + + 3 / 16 % ++ $ ->
      -17 $+0 
      --
    .
    ^

    10 IMG_out

    --
  .
  ^
.

# Outputs raw binary image RGB data.
IMG_toRAW:
  IMG_width IMG_height * 3 *

  ~8 $IMG_mem>_IMG_ptr ~0

  @'
    0
    ~8
      $_IMG_ptr $>_IMG_ptr >0
    ~0

    IMG_out
    --
  .
  ^
.

# Outputs image in ppm format.
IMG_toPPM:
  "P" IMG_out "6" IMG_out " " IMG_out
  IMG_width _IMG_outNum
  " " IMG_out
  IMG_height _IMG_outNum
  " " IMG_out "2" IMG_out "5" IMG_out "5" IMG_out
  10 IMG_out
  IMG_toRAW
.

# Pops 5, sets a pixel at $4 $3 to RGB($2,$1,$0).
IMG_setPixel:
  $4 $4 _IMG_coordsOK ?
    $4 $4 _IMG_seekPixel

    ~8 0 ~0
    $2 >8 ~8 $:_IMG_ptr $>_IMG_ptr 0 ~0
    $1 >8 ~8 $:_IMG_ptr $>_IMG_ptr 0 ~0
    >8 ^ ^ ~8 $:_IMG_ptr ~0

    ^ ^
  ;
    ^ ^ ^ ^ ^
  .
.

# Pops 2, pushes 3 values (RGB) of a pixel at $1 $0.
IMG_getPixel:
  $1 $1 _IMG_coordsOK ?
    $1 $1 _IMG_seekPixel

    0 ~8 $_IMG_ptr >0 $>_IMG_ptr ~0
    0 ~8 $_IMG_ptr >0 $>_IMG_ptr ~0
    0 ~8 $_IMG_ptr >0 ~0
  ;
    0 0 0
  .
.

# Pops 3, fills an image with RGB($2,$1,$0).
IMG_fill:
  IMG_width IMG_height *

  ~8 $IMG_mem>_IMG_ptr ~0

  @'
    ~8 0 ~0
    $1 >8 ~8 0 ~0
    $2 >8 ~8 0 ~0
    $3 >8

    ~8
      $:_IMG_ptr $>_IMG_ptr
      $:_IMG_ptr $>_IMG_ptr
      $:_IMG_ptr $>_IMG_ptr
    ~0

    --
  .
  ^

  ^ ^ ^
.

# Pops 7, draws a line from $6 $5 to $4 $3 with color RGB($2,$1,$0).
IMG_drawLine:
  $4 $7 - $:5       # dx
  $3 $6 - $:4       # dy

  $4 $0 * $4 $0 * > # abs(dx) > abs(dy)
    
  $0 $6 $6 ?? 0 << ?
    # swap coordinates
    $7 $6 + $:8     # x0 += dx
    $5 -1 * $:6     # dx *= -1

    $6 $5 + $:7     # y0 += dy
    $4 -1 * $:5     # dy *= -1
  . 

  $5 $0 0 << -1 1 ?? + $:6  # dx += dx < 0 ? -1 : 1
  $4 $0 0 << -1 1 ?? + $:5  # dy += ...
 
  0 # i

  # stack now: x0 y0 dx dy R G B abs(dx)>abs(dy) i  

  @@
    $5 $7         # dy dx

    $3 ?
      ><
    .

    $2 * >< //    # c = i * dy_dx / dx_dy
    $9 $9         # x y

    $4 ?
      $2 + >< $3 + ><   # x += i; y += c
    ;
      $3 + >< $2 + ><   # x += c; y += i
    .

    $7 $7 $7 IMG_setPixel ^

    ++
    $0 $2 $8 $8 ?? >= ?
      !@
    .
  .

  ^ ^ ^ ^ ^ ^ ^ ^ ^
.

# Pops 7, draws circle at $6 $5 with radius $4, width $3, color RGB($2,$1,$0).
IMG_drawCircle:
  $4 --
  @@     # for dx
    $5 --
    @@   # for dy
      # if dx^2 + dy^2 < r^2 && dx^2 + dy^2 > (r - w)^2
      $1 ++ $0 ++ * $1 ++ $0 ++ * + $0 $8 $0 * < >< $8 $8 - $0 * >> && ?
        $8 $2 + $8 $2 + $6 $6 $6 IMG_setPixel
        $8 $2 - $8 $2 + $6 $6 $6 IMG_setPixel
        $8 $2 + $8 $2 - $6 $6 $6 IMG_setPixel
        $8 $2 - $8 $2 - $6 $6 $6 IMG_setPixel
      .

      $0 0 = ?
        !@
      .

      --
    .
    ^
      
    $0 0 = ?
      !@
    .

    --
  .
  ^

  ^ ^ ^ ^ ^ ^ ^
.

# Pops 6, fills a circle at $5 $4 with radius $3 with color RGB($2,$1,$0).
IMG_fillCircle:
  $0 ^ ^ $0 ^ ^ $0 ^ ^ $0 $>0 $>0 $>0 IMG_drawCircle
.

# Outputs a number <= 100000 with the user defined function.
_IMG_outNum:
  10000

  @'
    $1 $1 / 0 != $1 1 = | ?
      !@
    .
    
    10 /
  .

  @'
    $1 $1 / "0" + IMG_out
    $1 $1 % $:2
    10 /
  .
  ^
  ^
.

# Checks if coords are within image.
_IMG_coordsOK:
  IMG_height < >< IMG_width < &
.

# Seeks to pixel at given coords.
_IMG_seekPixel:
  ~8
    $IMG_mem>_IMG_ptr
  ~0

  IMG_width * + 3 *
  
  @'
    ~8 $>_IMG_ptr ~0
    --
  .
  ^
.
